#include "proxy/conn/httpconn.h"
#include "proxy/conn/httpconnpair.h"

namespace proxy{

  HttpConn::Ptr HttpConn::CreateHttpConn( boost::asio::io_service& io_service, 
    socket_ptr socket, FlowInstance::Ptr flow_instance){
    return HttpConn::Ptr(new HttpConn(io_service, socket, flow_instance));
  }

  HttpConn::HttpConn(boost::asio::io_service& io_service, 
    socket_ptr socket, FlowInstance::Ptr flow_instance)
    :io_service_(io_service),
    socket_(socket),
    state_(CS_CONNECTED),
    remain_size_(0),
    flow_instance_(flow_instance){
  }

  HttpConn::~HttpConn(){
    //DLOG(WARNING) << "Delete Httpconn";
  }

  void HttpConn::Start(){
    socket_->async_read_some(
      boost::asio::buffer(buffer_, MAX_HTTP_BUFFER_SIZE),
      boost::bind(&HttpConn::OnHandleSocketRead, shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred));
  }

  void HttpConn::OnHandleSocketRead(const boost::system::error_code& err, int read_size){
    if (err){
      DLOG(ERROR) << err.message() << " " << bytebuffer_.Length() << " >> " 
        << http_parser_.http_request().method << " " 
        << http_parser_.http_request().host << " : "
        << http_parser_.http_request().port;
      return;
    }
    flow_instance_->AddUpFlow(read_size);
    //bytebuffer_.WriteBytes(buffer_, read_size);
    int buffer_size = read_size + remain_size_;

    int parser_size = http_parser_.Parse(buffer_, buffer_size);
    remain_size_ = buffer_size - parser_size;
    if (http_parser_.State() == PARSER_STATE_PARSERING){
      std::memmove(buffer_, buffer_ + parser_size, buffer_size - parser_size);
      // Need more data
      socket_->async_read_some(
        boost::asio::buffer(buffer_ + remain_size_, 
        MAX_HTTP_BUFFER_SIZE - remain_size_),
        boost::bind(&HttpConn::OnHandleSocketRead, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
    }
    else if (http_parser_.State() == PARSER_STATE_ERROR){
      DLOG(ERROR) << "Parser the http request error";
      DLOG(ERROR).write(bytebuffer_.Data(), bytebuffer_.Length());
      Respone400Error();
    }
    else{
      //SignalHttpMethodComplete(shared_from_this(), http_parser_.http_request());
      if (parser_size != buffer_size){
        std::memmove(buffer_, buffer_ + parser_size, buffer_size - parser_size);
      }
      HttpConnPair::Ptr http_conn_pair(new HttpConnPair(
        shared_from_this(), flow_instance_));
      http_conn_pair->Start();
    }
  }

  std::vector<boost::asio::const_buffer> HttpConn::to_proxy_buffers(){

    // DLOG(WARNING) << " START---------------------------";
    Request & request = http_parser_.http_request();

    std::vector<boost::asio::const_buffer> buffers;

    buffers.push_back(boost::asio::buffer(request.method));
    buffers.push_back(boost::asio::buffer(SPACE, 1));
    buffers.push_back(boost::asio::buffer(request.uri));
    buffers.push_back(boost::asio::buffer(HTTP_1_1));
    buffers.push_back(boost::asio::buffer(CRLF));
    DLOG(INFO) << request.method << SPACE << request.uri << HTTP_1_1 << CRLF;
    for (std::size_t i = 0; i < request.headers.size(); ++i)
    {
      header& h = request.headers[i];
      buffers.push_back(boost::asio::buffer(h.name));
      buffers.push_back(boost::asio::buffer(SEPARATOR));
      buffers.push_back(boost::asio::buffer(h.value));
      buffers.push_back(boost::asio::buffer(CRLF));
      //DLOG(INFO) << h.name << SEPARATOR << h.value << CRLF;
    }
    buffers.push_back(boost::asio::buffer(CRLF));
    //DLOG(INFO) << CRLF;
    if (remain_size_){
      buffers.push_back(boost::asio::buffer(buffer_, remain_size_));
      //DLOG(INFO).write(buffer_, remain_size_);
    }
    // DLOG(WARNING) << " END---------------------------";
    return buffers;
  }

  void HttpConn::Respone400Error(){
    reply_ = Reply::stock_reply(Reply::bad_request);
    AsyncReply(reply_);
  }

  void HttpConn::Respone404Error(){
    reply_ = Reply::stock_reply(Reply::not_found);
    AsyncReply(reply_);
  }

  void HttpConn::Respone503Error(){
    reply_ = Reply::stock_reply(Reply::service_unavailable);
    AsyncReply(reply_);
  }

  void HttpConn::AsyncReply(Reply& reply){
    boost::asio::async_write(*socket_, reply.to_buffers(),
      boost::bind(&HttpConn::HandleAsyncWrite, shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred));
  }

  void HttpConn::HandleAsyncWrite(boost::system::error_code err, int size){
    // Initiate graceful connection closure.
    flow_instance_->AddDownFlow(size);
    socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both, err);
    DLOG(INFO) << "Socket shutdown_both";
  }
}